package com.hcss.common.util;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.hcss.common.constant.CommonConstants;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hcss.common.vo.ResultMsg;
import com.hcss.common.vo.Results;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class HttpClientUtil {

    private static final Object cmLock = new Object();
    private static volatile PoolingHttpClientConnectionManager cm;
    private static final int downloadTimeout = 60*1000;

    private static void init() {
        if (null == cm) {
            synchronized(cmLock){
                if(null == cm){
                    cm = new PoolingHttpClientConnectionManager();
                    cm.setMaxTotal(50);// 整个连接池最大连接数
                    cm.setDefaultMaxPerRoute(50);// 每路由最大连接数，默认值是2
                }
            }
        }
    }

    /**
     * 通过连接池获取HttpClient
     *
     * @return
     */
    private static CloseableHttpClient getHttpClient() {
        init();
        return HttpClients.custom().setConnectionManager(cm).build();
    }

    /**
     * 处理Http请求
     *
     * @param request
     * @return
     */
    private static String getResult(HttpRequestBase request) {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            CloseableHttpResponse response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity, "UTF-8");
                response.close();
                return result;
            }
        } catch (Exception e) {
            log.error("处理http请求出错， ");
        }

        return "";
    }

    /**
     * 处理post请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String doPost(String url, String params) {
        HttpPost httpPost = new HttpPost(url);

        StringEntity entity = new StringEntity(params,"utf-8");
        entity.setContentEncoding("UTF-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);

        return getResult(httpPost);
    }

    /**
     * 处理POST请求，返回clazz格式数据
     *
     * @param url
     * @param params
     * @param clazz
     * @return
     */
    public static<T> T doPostObject(String url, Map<String, String> params, Class<T> clazz) throws Exception{
        return JSON.parseObject(doPost(url, params), clazz);
    }

    /**
     * 带参数POST
     * @param url
     * @param params
     * @return
     * @throws Exception
     */
    public static String doPost(String url, Map<String, String> params) throws Exception {
        if (StringUtils.isBlank(url)) {
            throw new Exception("URL请求地址为空!");
        }
        url = urlTransCodeing(url);
        HttpPost httpPost = new HttpPost(url);
        if (params != null && !params.isEmpty()) {
            List<NameValuePair> formParams = new ArrayList<NameValuePair>(); // 构建POST请求的表单参数
            for (Map.Entry<String, String> entry : params.entrySet()) {
                formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            if(formParams != null && formParams.size() > 0){
                httpPost.setEntity(new UrlEncodedFormEntity(formParams, "UTF-8"));
            }
        }
        CloseableHttpClient httpClient = getSslHttpClient();
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
                httpPost.abort();
                throw new Exception("HttpUtils->Post," + "url:" + url + ",error status code:" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, "UTF-8");
            EntityUtils.consume(entity);
            return result;
        } finally {
            response.close();
            httpClient.close();
        }
    }

    /**
     * 处理POST请求，返回JOSN格式数据
     *
     * @param url
     * @param params --json字符串
     * @return
     */
    public static JSONObject doPostJSON(String url, String params) {
        return JSON.parseObject(doPost(url, params));
    }

    /**
     * 处理POST请求，返回clazz格式数据
     *
     * @param url
     * @param params
     * @param clazz
     * @return
     */
    public static<T> T doPostObject(String url, String params, Class<T> clazz) {
        return JSON.parseObject(doPost(url, params), clazz);
    }

    /**
     * 处理get请求
     *
     * @param url
     * @return
     */
    public static String doGet(String url) {
        HttpGet httpGet = new HttpGet(url);
        return getResult(httpGet);
    }

    /**
     * 处理get请求，返回JOSN格式数据
     *
     * @param url
     * @return
     */
    public static JSONObject doGetJSON(String url){
        return JSONObject.parseObject(doGet(url));
    }

    /**
     * 处理get请求，返回clazz格式数据
     * @param url
     * @param clazz
     * @return
     */
    public static<T> T doGetObject(String url, Class<T> clazz) {
        return JSON.parseObject(doGet(url), clazz);
    }

    /**
     * POST请求上传文件
     * @param file_path
     * @param url
     * @return
     * @throws Exception
     */
    public static JSONObject doMultipartPost(String file_path, String url) throws Exception {
        File file = new File(file_path);
        String fileName = file_path.substring(file_path.lastIndexOf("/")+1);
        URL urlObj = new URL(url);
        // 连接
        HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
        con.setRequestMethod("POST"); // 以Post方式提交表单，默认get方式
        con.setDoInput(true);
        con.setDoOutput(true);
        con.setUseCaches(false); // post方式不能使用缓存
        // 设置请求头信息
        con.setRequestProperty("Connection", "Keep-Alive");
        con.setRequestProperty("Charset", "UTF-8");
        // 设置边界
        String BOUNDARY = "----------" + System.currentTimeMillis();
        con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        // 请求正文信息
        // 第一部分：
        StringBuilder sb = new StringBuilder();
        sb.append("--"); // 必须多两道线
        sb.append(BOUNDARY);
        sb.append("\r\n");
        sb.append("Content-Disposition: form-data;name=\"media\";filename=\""
                + fileName + "\"\r\n");
        sb.append("Content-Type:application/octet-stream\r\n\r\n");
        byte[] head = sb.toString().getBytes( StandardCharsets.UTF_8 );
        // 获得输出流
        OutputStream out = con.getOutputStream();//new DataOutputStream(con.getOutputStream());
        // 输出表头
        out.write(head);
        // 文件正文部分
        // 把文件已流文件的方式 推入到url中
        //DataInputStream in = new DataInputStream(new FileInputStream(file));
        FileInputStream in = new FileInputStream(file);
        int bytes = 0;
        byte[] bufferOut = new byte[1024];
        while ((bytes = in.read(bufferOut)) != -1) {
            out.write(bufferOut, 0, bytes);
        }
        in.close();
        // 结尾部分
        byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes( StandardCharsets.UTF_8 );// 定义最后数据分隔线
        out.write(foot);
        out.flush();
        out.close();
        StringBuffer buffer = new StringBuffer();
        BufferedReader reader = null;
        String result = null;
        try {
            // 定义BufferedReader输入流来读取URL的响应
            reader = new BufferedReader(new InputStreamReader(
                    con.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            result = buffer.toString();
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException("数据读取异常", e);
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
        return JSONObject.parseObject(result);
    }

    /**
     * POST请求上传图片文件
     * @param file
     * @param fileName
     * @param url
     * @return
     * @throws Exception
     */
    public static JSONObject doMultipartPost(InputStream file, String fileName, String url) throws Exception{
        URL urlObj = new URL(url);
        int fileLength = file.available();
        // 连接
        HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
        con.setRequestMethod("POST"); // 以Post方式提交表单，默认get方式
        con.setDoInput(true);
        con.setDoOutput(true);
        con.setUseCaches(false); // post方式不能使用缓存
        // 设置请求头信息
        con.setRequestProperty("Connection", "Keep-Alive");
        con.setRequestProperty("Charset", "UTF-8");
        // 设置边界
        String BOUNDARY = "----------" + System.currentTimeMillis();
        con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        // 请求正文信息
        // 第一部分：
        StringBuilder sb = new StringBuilder();
        sb.append("--"); // 必须多两道线
        sb.append(BOUNDARY);
        sb.append("\r\n");
        sb.append("Content-Disposition: form-data;name=\"media\";filename=\""
                + fileName + "\";filelength=" + fileLength + "\r\n");
        sb.append("Content-Type:application/octet-stream\r\n\r\n");
        byte[] head = sb.toString().getBytes( StandardCharsets.UTF_8 );
        // 获得输出流
        OutputStream out = new DataOutputStream(con.getOutputStream());
        // 输出表头
        out.write(head);
        // 文件正文部分
        // 把文件已流文件的方式 推入到url中
        DataInputStream in = new DataInputStream(file);
        int bytes = 0;
        byte[] bufferOut = new byte[1024];
        while ((bytes = in.read(bufferOut)) != -1) {
            out.write(bufferOut, 0, bytes);
        }
        in.close();
        // 结尾部分
        byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes( StandardCharsets.UTF_8 );// 定义最后数据分隔线
        out.write(foot);
        out.flush();
        out.close();
        StringBuffer buffer = new StringBuffer();
        BufferedReader reader = null;
        String result = null;
        try {
            // 定义BufferedReader输入流来读取URL的响应
            reader = new BufferedReader(new InputStreamReader(
                    con.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            result = buffer.toString();
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException("数据读取异常", e);
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
        return JSON.parseObject(result);
    }

    /**
     * 将返回的请求流中的文件保存到本地服务器
     * @param url
     * @param postData
     * @param savePath
     * @param fileName
     * @return
     * @throws Exception
     */
    public static ResultMsg doPostFile(String url, String postData, String savePath, String fileName) throws Exception {
        ResultMsg resultMsg = new ResultMsg();
        if (StringUtils.isBlank(url)) {
            throw new Exception("URL请求地址为空!");
        }
        url = urlTransCodeing(url);
        HttpPost httpPost = new HttpPost(url);
        StringEntity postDataEntity = new StringEntity(postData,"utf-8");//解决中文乱码问题
        postDataEntity.setContentEncoding("UTF-8");
        postDataEntity.setContentType("application/json");
        httpPost.setEntity(postDataEntity);
        CloseableHttpClient httpClient = getSslHttpClient();
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
                httpPost.abort();
                throw new Exception("HttpUtils->PostJson," + "url:" + url + ",error status code:" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String contentType = entity.getContentType().getValue();
            // 判断返回的是否为图片文件流
            if (contentType.contains("image")) {
                // 获取文件流
                InputStream input = entity.getContent();
                // 获取文件后缀
                Header h = response.getAllHeaders()[2];
                String hvalue = new String(h.getValue().getBytes(),StandardCharsets.UTF_8 );
                String suffix = hvalue.substring(hvalue.lastIndexOf(".") + 1, hvalue.length() - 1);
                // 建立文件夹
                File path = new File(savePath);
                if (!path.exists()) {
                    path.mkdirs();
                }
                String realName = fileName + "." + suffix;
                // 新建文件
                File file = new File(path, realName);
                // 写入文件数据
                OutputStream os = new FileOutputStream(file);
                int bytesRead = 0;
                byte[] buffer = new byte[8192];
                while ((bytesRead = input.read(buffer, 0, 8192)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.close();
                input.close();
                resultMsg.setState(Results.SUCCESS);
                resultMsg.setEntity(realName);
            } else {
                String resultStr = EntityUtils.toString(entity, "UTF-8");
                com.alibaba.fastjson.JSONObject result = JSON.parseObject(resultStr);
                resultMsg.setState(Results.ERROR);
                resultMsg.setEntity(result);
            }
            return resultMsg;
        } finally {
            response.close();
            httpClient.close();
        }
    }

    /**
     * 以get的方式请求资源
     * 成功返回下载后的资源文件位置
     * 如果返回的响应响应头中有对应文件名称信息的话直接拿出，否则使用src_file的hashcode作为文件名称了。
     * @param url
     * @return
     */
    public static String downloadFile(String src_file, String dest_file) throws Throwable {
        log.info("请求" + src_file + "资源...");
        src_file = urlTransCodeing(src_file);
        String filename = null;
        try (CloseableHttpClient httpclient = getSslHttpClient()) {
            HttpGet httpget = new HttpGet(src_file);
            httpget.setConfig(RequestConfig.custom()
                    .setConnectionRequestTimeout(downloadTimeout)
                    .setConnectTimeout(downloadTimeout)
                    .setSocketTimeout(downloadTimeout)
                    .build());
            try (CloseableHttpResponse response = httpclient.execute(httpget)) {
                HttpEntity entity = response.getEntity();
                // 拿响应头相关信息
                Header contentHeader = response.getFirstHeader("Content-disposition");
                if (contentHeader != null) {
                    HeaderElement[] values = contentHeader.getElements();
                    if (values.length == 1) {
                        NameValuePair param = values[0].getParameterByName("filename");
                        if (param != null) {
                            // 注意响应头中乱码的解决，httpclient默认使用IOS-8859-1进行编码
                            filename = new String(param.getValue().getBytes( StandardCharsets.ISO_8859_1 ),StandardCharsets.UTF_8 );
                            int index = filename.lastIndexOf("/");
                            if(index != -1) {
                            	 Header header = entity.getContentType();
                            	 String type = header.getValue();
                            	 if(StringUtils.isNotEmpty(type)) {
                            		 type = type.substring(type.lastIndexOf("/") + 1 );
                            	 }
                        		 filename = filename.substring(index + 1 ) + "." + type;
                            }
                        }
                    }
                }
                File desc = new File(dest_file + File.separator + filename);
                File folder = desc.getParentFile();
                if(!folder.exists()) {
                    folder.mkdirs();
                }
                try (InputStream is = entity.getContent();
                     OutputStream os = new FileOutputStream(desc)) {
                    byte[] buf = new byte[2048];
                    int bytesRead = is.read(buf);
                    while (bytesRead != -1) {
                        os.write(buf, 0, bytesRead);
                        bytesRead = is.read(buf);
                    }
                    os.flush();
                }
            } catch (Throwable e) {
                log.error("文件下载失败", e);
                throw new Throwable("文件下载失败......", e);
            }
        }
        if(filename == null) {
            filename = String.valueOf(src_file.hashCode());
        }
        return dest_file + File.separator + filename;
    }

    public static void main(String[] args) throws Throwable {

    	String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get?" +
                "access_token=uNQpc-khbmVKZfKkvj6NhpwVzqe-H1dm95Ue1vjNp4LfcbPcxJhB8MqToXd8xCFp2ZeaPMY1K7WwbDXborXVxMg68hrgWYwZblMowWewfWIhvoRfvf_Ih4_M4FuU4qsHt15hoGom551anWrXVN1W1WDVQHUulKoj90-nuLNQ4HRJNQoILjGtmGIkuMauAiw0hK9syvgBetMgfvJnw7rrJw" +
                "&media_id=1tr3umCXah3DS9Vp2TGqQ3AM03HJWpUVtMoE6YtsFZMB1PTFlq8ERCyonBdSekcAk";
        downloadFile(url, "D:/upload/event");


//        downloadFile("https://code.jquery.com/jquery-3.3.1.min.js", "d:/");
    }

    private static String urlTransCodeing(String url){
        return url.replace("\r\n", "").replace("\"", "%22").replace("{", "%7b").replace("}", "%7d").replace("|", "%124"); //.replace("&", "%26")
    }

    private static CloseableHttpClient getSslHttpClient() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }
            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }
        }};
        SSLContext sc = SSLContext.getInstance("TLSv1");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        SSLConnectionSocketFactory scsf = new SSLConnectionSocketFactory(sc, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        RequestConfig config = RequestConfig.custom().setConnectTimeout(20000).setSocketTimeout(30000).build();
        CloseableHttpClient httpclient  = HttpClients.custom().setSSLSocketFactory(scsf).setDefaultRequestConfig(config).build();
        return httpclient;
    }

}

